﻿////////////////////////////////////////////
//程序名称：任意直线的对称变换
//功    能：可实现任意直线的对称变换
//编译环境：Microsoft Visual Studio 2017 , EasyX春分版
//作    者：唐瑞宏<2018301010303><2044291597@qq.com>
//最后修改：2020-3-29
#include<iostream>
#include<graphics.h>
#include<vector>
#include<math.h>
#include"test.h"
using namespace std;
//关于vector，运行速度上略低于数组，但release时可以接受，debug时因调试代码较多而性能损失较大

//通过拖拽的形式画线，并传出线段起点和终点
void line_dragcreate(int &x1, int &y1, int &x2, int &y2, COLORREF color ) {
	MOUSEMSG mouse;
	COLORREF back = getbkcolor();//方便以后擦除墨迹
	int flag = 0, t1, t2, d1, d2;
	x1 = 0, y1 = 0;
	while (true) {
		mouse = GetMouseMsg();
		if (mouse.uMsg == WM_LBUTTONDOWN) {
			x1 = mouse.x;y1 = mouse.y; flag++; //按下鼠标左键，开始拖动画矩形，且只触发一次
			t1 = x1;t2 = y1;
		}
		//开始拖拽
		if (mouse.uMsg == WM_MOUSEMOVE && flag) {
			if (t1 - x1 < 0)d1 = -1;//这四行代码是用来支持向各个距离拖拽的
			else d1 = 1;
			if (t2 - y1 < 0)d2 = -1;
			else d2 = 1;
			BeginBatchDraw();//让绘图丝般顺滑~
			mid_drawline(x1, y1, t1, t2, back);//擦除墨迹
			FlushBatchDraw();
			EndBatchDraw();
			t1 = mouse.x;t2 = mouse.y;//目前鼠标位置
			if (t1 - x1 < 0)d1 = -1;
			else d1 = 1;
			if (t2 - y1 < 0)d2 = -1;
			else d2 = 1;
			BeginBatchDraw();
			mid_drawline(x1, y1, t1, t2, color);//绘制直线
			FlushBatchDraw();
			EndBatchDraw();
			FlushMouseMsgBuffer();
		}
		else if (mouse.uMsg == WM_LBUTTONUP) {
			FlushMouseMsgBuffer();
			x2 = mouse.x;y2 = mouse.y;
			break;
		}
		FlushMouseMsgBuffer();
	}
}
//通过右键拖拽的形式画对称线，并传出线段起点和终点(1)，如果仍试图左键拖拽，将退出函数(0)，以开始新的画线
int symline_dragcreate(int &xt, int &yt, int &xt1, int &yt1) {
	MOUSEMSG mouse;
	COLORREF back = getbkcolor();//方便以后擦除墨迹
	int flag = 0, t1, t2, d1, d2;
	xt = 0, yt = 0;
	while (true) {
		mouse = GetMouseMsg();
		if (mouse.uMsg == WM_RBUTTONDOWN) {
			xt = mouse.x;yt = mouse.y; flag++; //按下鼠标左键，开始拖动画矩形，且只触发一次
			t1 = xt;t2 = yt;
		}
		//开始拖拽
		if (mouse.uMsg == WM_MOUSEMOVE && flag) {
			if (t1 - xt < 0)d1 = -1;//这四行代码是用来支持向各个距离拖拽的
			else d1 = 1;
			if (t2 - yt < 0)d2 = -1;
			else d2 = 1;
			BeginBatchDraw();//让绘图丝般顺滑~
			mid_drawline(xt, yt, t1, t2, back);//擦除墨迹
			FlushBatchDraw();
			EndBatchDraw();
			t1 = mouse.x;t2 = mouse.y;//目前鼠标位置
			if (t1 - xt < 0)d1 = -1;
			else d1 = 1;
			if (t2 - yt < 0)d2 = -1;
			else d2 = 1;
			BeginBatchDraw();
			mid_drawline(xt, yt, t1, t2, YELLOW);//绘制直线
			FlushBatchDraw();
			EndBatchDraw();
			FlushMouseMsgBuffer();
		}
		else if (mouse.uMsg == WM_RBUTTONUP) {
			xt1 = mouse.x;yt1 = mouse.y;
			break;
		}
		else if (mouse.uMsg == WM_LBUTTONUP) { return 0; }
		FlushMouseMsgBuffer();
	}
	return 1;
}
int main(int argc, char**argv) {
	cout << "请先按鼠标左键，拖拽画出白线，然后您可以点击一下鼠标左键，继续画白线\n您也可以按住鼠标右键，拖拽划黄线\n"
		<< "黄线为对称轴，白线为目标线，当黄线画完后，会显示画出来的对称线，您按任意键后，会将绘图框重置！\n";
	system("pause");
	vector<int> lines;
	Matrixf points, transla, transla2, rotate, rotate2, sym;//第二个是平移，第四个是旋转，最后是对称
	int x1, y1, x2, y2, xt, yt, xt1, yt1 , flag= 0;
	float sina, cosa, l;
	int dxt, dyt;
	sym.push_back({ 1,0,0 });sym.push_back({ 0,-1,0 });sym.push_back({ 0,0,1 });//sym
	initgraph(1300, 800);
	while (1) {
		if (flag) {//如果已经执行对称，将重置画板
			cleardevice();lines.clear();points.clear();
		}
		line_dragcreate(x1, y1, x2, y2, WHITE);//画目标线
		lines.push_back(x1);lines.push_back(y1);lines.push_back(x2);lines.push_back(y2);//记录线条
		for (auto i = lines.begin();i != lines.end();i+=4) {//再画一遍线条，防止误伤
			mid_drawline((*i), *(i+1), *(i+2), *(i+3));
		}
		flag = symline_dragcreate(xt, yt, xt1, yt1);// 画对称轴，如果flag==1,将变换
		points.push_back({ (float)x1,(float)y1,1 });//构建points矩阵
		points.push_back({ (float)x2,(float)y2,1 });

		if (flag) {//准备执行对称
			dxt = xt1 - xt;dyt = yt1 - yt;
			l = sqrt(dxt*dxt + dyt * dyt);
			sina = dyt / l;cosa = dxt / l;//求sin、cos
			transla.push_back({1,0,0});				//transla
			transla.push_back({0,1,0});
			transla.push_back({ -(float)xt,-(float)yt,1 });
			transla2.push_back({ 1,0,0 });			//transla2
			transla2.push_back({ 0,1,0 });
			transla2.push_back({ (float)xt,(float)yt,1 });
			rotate.push_back({ cosa,sina,0 });		//rotate
			rotate.push_back({ -sina,cosa,0 });
			rotate.push_back({ 0,0,1 });
			rotate2.push_back({ cosa,-sina,0 });	//rotate2
			rotate2.push_back({ sina,cosa,0 });
			rotate2.push_back({ 0,0,1 });
			
			points =points* transla * rotate2 * sym * rotate * transla2;
			for (auto it = points.begin();it != points.end();it+=2) {
				mid_drawline((*it)[0], (*it)[1], (*(it + 1))[0], (*(it + 1))[1]);
			}
			system("pause");
		}
	}
	return 0;
}